﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.IO;
using System.Net;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Management;
using Microsoft.VisualBasic.Devices;
using System.Threading;

namespace RCT2
{
    public partial class Bars : Form
    {
        #region fixes

        //Hide the form from the ALT-TAB menu
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int W, int H, uint uFlags);
        public static IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        [DllImport("dwmapi.dll", PreserveSig = false)]
        public static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
        [Flags]
        public enum DWMWINDOWATTRIBUTE
        {
            DWMWA_NCRENDERING_ENABLED = 1,
            DWMWA_NCRENDERING_POLICY,
            DWMWA_TRANSITIONS_FORCEDISABLED,
            DWMWA_ALLOW_NCPAINT,
            DWMWA_CAPTION_BUTTON_BOUNDS,
            DWMWA_NONCLIENT_RTL_LAYOUT,
            DWMWA_FORCE_ICONIC_REPRESENTATION,
            DWMWA_FLIP3D_POLICY,
            DWMWA_EXTENDED_FRAME_BOUNDS,
            DWMWA_HAS_ICONIC_BITMAP,
            DWMWA_DISALLOW_PEEK,
            DWMWA_EXCLUDED_FROM_PEEK,
            DWMWA_LAST
        }
        [Flags]
        public enum DWMNCRenderingPolicy
        {
            UseWindowStyle,
            Disabled,
            Enabled,
            Last
        }

        //Keeps this window visible when "peeking" at the desktop
        public void RemoveFromAeroPeek(IntPtr Hwnd)
        {
            int renderPolicy = (int)DWMNCRenderingPolicy.Enabled;
            DwmSetWindowAttribute(Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_EXCLUDED_FROM_PEEK, ref renderPolicy, sizeof(int));
        }

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr window, int index, int value);
        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr window, int index);
        [DllImport("user32.dll")]
        static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);

        [DllImport("user32.dll")]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        [DllImport("user32.dll")]
        public static extern IntPtr FindWindowA(string ClassName, string WindowName);


        public const int WS_EX_LAYERED = 0x80000;
        public const int LWA_ALPHA = 0x2;
        public const int LWA_COLORKEY = 0x1;
        const int GWL_EXSTYLE = -20;
        const int WS_EX_TOOLWINDOW = 0x00000080;
        const int WS_EX_APPWINDOW = 0x00040000;
        #endregion

        SettingsEditor settingsEditor;
        PositionHelper posHelper;

        PerformanceCounter cpuCounter;
        PerformanceCounter diskCounter;
        //ManagementObjectSearcher mySearcher;
        public bool needsUpdate = false;
        double twoPercent;
        double onePercent;
        double totalMemory;
        int fadeTicks = 20;

        int twoLastColor = -1;
        int oneLastColor = -1;
        Color oneNC, twoNC;
        int onedr, onedg, onedb, twodr, twodg, twodb;

        public Bars()
        {
            SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) ^ WS_EX_LAYERED);
            SetLayeredWindowAttributes(Handle, 0, 128, LWA_ALPHA);

            IntPtr progmanHwnd = FindWindowA("Progman", "Program Manager");
            SetParent(this.Handle, progmanHwnd);

            cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
            diskCounter = new PerformanceCounter("PhysicalDisk", "% Disk Time", "_Total");

            //mySearcher = new ManagementObjectSearcher("root\\WMI", "SELECT * from MSAcpi_ThermalZoneTemperature");

            InitializeComponent();

            RemoveFromAeroPeek(this.Handle);
            int windowStyle = GetWindowLong(this.Handle, GWL_EXSTYLE);
            SetWindowLong(this.Handle, GWL_EXSTYLE, windowStyle | WS_EX_TOOLWINDOW);

            contextMenu1.MenuItems.Add("v2.2.2");
            contextMenu1.MenuItems.Add("Settings");
            contextMenu1.MenuItems[1].Click += Bars_Click;
            contextMenu1.MenuItems.Add("Exit");
            contextMenu1.MenuItems[2].Click += Bars2_Click;

            notifyIcon1.ContextMenu = contextMenu1;

            posHelper = new PositionHelper();
            settingsEditor = new SettingsEditor(this);
            Settings.readSettings();

            Settings.side = posHelper.TaskbarSide();
            updateMemoryData(Settings.memType);
            updateFormLocation();
            calculateOne();
            calculateTwo();
            oneTimer.Start();
            twoTimer.Start();

            if (Settings.OptionsAtStartup) settingsEditor.ShowDialog();
            if (Settings.autoUpdate)
            {
                if ((DateTime.Now - Settings.lastUpdate).Days >= 1)
                {
                    Thread update = new Thread(new ParameterizedThreadStart(checkUpdates));
                    update.Start((object)false);
                }
            }
        }

        public void checkUpdates(object _forced)
        {
            bool forced = (bool)_forced;
            try
            {
                WebClient wc = new WebClient();
                Version cv = new Version(Application.ProductVersion);
                string version = wc.DownloadString("https://dl.dropboxusercontent.com/s/yep35r945eyocir/version.html"); version = version.Remove(version.IndexOf("\r\n<!-- Hosting24 "));
                Version nv = new Version(version);
                if (nv > cv)
                    if (MessageBox.Show("A new version is available. Would you like to download and apply the update now?", "Updater", MessageBoxButtons.YesNo) == DialogResult.Yes)
                    {
                        File.Move(Application.ExecutablePath, Path.GetDirectoryName(Application.ExecutablePath) + "\\RCT2_old.exe");
                        wc.DownloadFile("https://dl.dropbox.com/s/sx5ip8sep20pk67/RCT2.exe", Path.GetDirectoryName(Application.ExecutablePath) + "\\RCT2.exe");
                        System.Diagnostics.Process.Start(Path.GetDirectoryName(Application.ExecutablePath) + "\\RCT2.exe", "wait");
                        notifyIcon1.Visible = false;
                        Environment.Exit(0);
                    }
                    else
                        if (forced) MessageBox.Show("No updates are available at the current time.", "Updater");
                Settings.lastUpdate = DateTime.Now;
                Settings.writeSettings();
            }
            catch
            {
                if (forced) MessageBox.Show("Could not check for updates. Please check your internet connection (or your proxy settings).");
            }
        }

        public void setTimers()
        {
            oneTimer.Interval = Settings.oneSettings.timer;
            twoTimer.Interval = Settings.twoSettings.timer;
        }

        public void updateMemoryData(int type) //0 Total, 1 Physical, 2 Virtual
        {
            ComputerInfo compInfo = new ComputerInfo();
            if (type == 0)
                totalMemory = (compInfo.TotalVirtualMemory + compInfo.TotalPhysicalMemory);
            else if (type == 1)
                totalMemory = compInfo.TotalPhysicalMemory;
            else
                totalMemory = compInfo.TotalVirtualMemory;

            //foreach (ManagementBaseObject item in searcher.Get())
            //{
            //    if (type == 2)
            //        totalMemory = (int)item["TotalVirtualMemory"];
            //    else if (type == 1)
            //        totalMemory = (int)item["TotalPhysicalMemory"];
            //    else
            //        totalMemory = (int)item["TotalPageFileSpace"];
            //}
        }

        public void updateBarLocations()
        {
            if (Settings.oneSettings.varIndex == Settings.twoSettings.varIndex) //If single bar only
            {
                oneTimer.Start();

                //Remove bar 2, only use bar 1
                twoTimer.Stop();
                bar2.Width = 0; bar2.Height = 0;
                bar2.BackColor = Color.FromArgb(168, 63, 110);
                //Make the ram bar cover the whole area
                if (Settings.side == 0 || Settings.side == 2) bar1.Height = this.Height;
                else bar1.Width = this.Width;

                return;
            }
            else
                twoTimer.Start(); //If timer is already running it will have no effect

            if (Settings.side == 0 || Settings.side == 2) //still need to take into account whether or not the taskbar is on the left or right
            {
                //First, is it split by height or width?

                if (Settings.splitByHeight)
                {
                    //Change the height of the bars to exactly half the taskbar
                    bar1.Height = bar2.Height = posHelper.TaskbarSize().Height / 2;

                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, 0); //Set it to top left
                    else bar1.Location = new Point(this.Width - bar1.Width, 0); //Set it to the width - the value (so basically it hugs the right wall of the form, on the top row)
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(0, bar1.Height); //You could also do height - cpuBar.height
                    else bar2.Location = new Point(this.Width - bar2.Width, bar1.Height); //Yeah, you get the point. Same as rambar, but just y-offset by the height of the rambar
                }
                else
                {
                    //Make the height to be the same as the taskbar, and split the width in 2 (to make it half the taskbar width, to fit 2 of the bars in)
                    bar1.Height = bar2.Height = posHelper.TaskbarSize().Height;

                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, 0);
                    else bar1.Location = new Point((this.Width / 2) - bar1.Width, 0);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(this.Width / 2, 0);
                    else bar2.Location = new Point(this.Width - bar2.Width, 0);
                }
            }
            else
            {
                //Split by width or height? 
                //When the taskbar is on the sides, split by width and height have different meanings as the bars are going up and down as they progress rather than left and right.
                if (!Settings.splitByHeight) //For that reason, I am doing splitbyWidth first here... same thing as splitByHeight NORMALLY, just rotated.
                {
                    bar1.Width = bar2.Width = this.Width / 2;

                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, this.Height - bar1.Height);//We will treat "left" as "bottom" when the taskbar is on the sides
                    else bar1.Location = new Point(0, 0);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(bar1.Width, this.Height - bar2.Height);
                    else bar2.Location = new Point(bar1.Width, 0);
                }
                else
                {
                    bar1.Width = bar2.Width = this.Width;

                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, (this.Height / 2) - bar1.Height);
                    else bar1.Location = new Point(0, 0);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(0, this.Height - bar2.Height);
                    else bar2.Location = new Point(0, this.Height / 2);
                }
            }
        }

        public void updateFormLocation()
        {
            Settings.side = posHelper.TaskbarSide();
            if (Settings.HideIcon) notifyIcon1.Visible = false;
            else notifyIcon1.Visible = true;
            this.Location = posHelper.TaskbarPosition(); //set the position of the form to the taskbar position
            this.Size = posHelper.TaskbarSize();//set the size of the form to the same size as the taskbar
            settingsEditor.changeLabel();
            updateBarLocations();
            setTimers();
            oneLastColor = -1;
            twoLastColor = -1;

            //update it now at t=0 so the change isn't delayed till next tick, just in case tick time is long
            calculateOne();
            if (Settings.oneSettings.varIndex != Settings.twoSettings.varIndex) calculateTwo();

            needsUpdate = false;
        }

        private void positionTimer_Tick(object sender, EventArgs e)
        {
            Point taskPosition = posHelper.TaskbarPosition();
            if (this.Location != taskPosition)
                needsUpdate = true;
            if (needsUpdate) updateFormLocation();
        }

        private void ramTimer_Tick(object sender, EventArgs e)
        {
            calculateOne();
        }

        private void cpuTimer_Tick(object sender, EventArgs e)
        {
            calculateTwo();
        }

        double getPercent(int index) //Make sure to return as a decimal not percent
        {
            if (index == 0) //RAM
            {
                ComputerInfo info = new ComputerInfo();
                double available = info.AvailablePhysicalMemory;
                return ((totalMemory - available) / totalMemory); //Total - available = used
            }
            else if (index == 1) //CPU
            {
                return (cpuCounter.NextValue() / 100);
            }
            else if (index == 2) //Disk Usage
            {
                return (diskCounter.NextValue() / 100);
            }
            else if (index == 3) //Temperature
            {
                //foreach (ManagementObject obj in mySearcher.Get())
                //{
                //    if (obj != null)
                //        return obj["CurrentTemperature"];
                //}

            }
            return 0;
        }

        void calculateOne()
        {
            //Get usage!
            onePercent = getPercent(Settings.oneSettings.varIndex);
            //Change the colors

            int color;

            if (onePercent * 100 > Settings.oneSettings.percents[1]) color = 2;
            else if (onePercent * 100 > Settings.oneSettings.percents[0]) color = 1;
            else color = 0;

            //Fading effect
            if (color != oneLastColor)
            {
                if (Settings.fade)
                {
                    oneNC = Settings.oneSettings.colours[color];
                    onedr = (int)Math.Round(((double)oneNC.R - (double)bar1.BackColor.R) / (double)fadeTicks);
                    onedg = (int)Math.Round(((double)oneNC.G - (double)bar1.BackColor.G) / (double)fadeTicks);
                    onedb = (int)Math.Round(((double)oneNC.B - (double)bar1.BackColor.B) / (double)fadeTicks);

                    oneFadeTimer.Start();
                }
                else
                    bar1.BackColor = Settings.oneSettings.colours[color];
            }
            oneLastColor = color;


            //Change width/height of the bar relative to where it is positioned
            #region dimensionChanges
            if (Settings.side == 0 || Settings.side == 2)
            {
                if (Settings.oneSettings.varIndex == Settings.twoSettings.varIndex || Settings.splitByHeight) //If ram only
                {
                    bar1.Width = (int)(onePercent * this.Width);
                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, 0); //Set it to top left
                    else bar1.Location = new Point(this.Width - bar1.Width, 0); //Set it to the width - the value (so basically it hugs the right wall of the form, on the top row)
                }
                else
                {
                    bar1.Width = (int)(onePercent * (this.Width / 2));
                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, 0);
                    else bar1.Location = new Point((this.Width / 2) - bar1.Width, 0);
                }
            }
            else
            {
                if (Settings.oneSettings.varIndex == Settings.twoSettings.varIndex || !Settings.splitByHeight) //If single bar only
                {
                    bar1.Height = (int)(onePercent * this.Height);
                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, this.Height - bar1.Height);
                    else bar1.Location = new Point(0, 0);
                }
                else
                {
                    bar1.Height = (int)(onePercent * (this.Height / 2));
                    if (Settings.oneSettings.leftAligned) bar1.Location = new Point(0, (this.Height / 2) - bar1.Height);
                    else bar1.Location = new Point(0, 0);
                }
            }
            #endregion
        }

        void calculateTwo()
        {
            //Get bar 2 usage
            twoPercent = getPercent(Settings.twoSettings.varIndex); 
            //Change the colors
            int color;

            if (twoPercent * 100 > Settings.twoSettings.percents[1]) color = 2;
            else if (twoPercent * 100 > Settings.twoSettings.percents[0]) color = 1;
            else color = 0;

            if (color != twoLastColor)
            {
                if (Settings.fade)
                {
                    twoNC = Settings.twoSettings.colours[color];
                    twodr = (int)Math.Round(((double)twoNC.R - (double)bar2.BackColor.R) / (double)fadeTicks);
                    twodg = (int)Math.Round(((double)twoNC.G - (double)bar2.BackColor.G) / (double)fadeTicks);
                    twodb = (int)Math.Round(((double)twoNC.B - (double)bar2.BackColor.B) / (double)fadeTicks);

                    twoFadeTimer.Start();
                }
                else
                    bar2.BackColor = Settings.twoSettings.colours[color];
            }
            twoLastColor = color;

            #region dimensionChanges
            if (Settings.side == 0 || Settings.side == 2)
            {
                if (Settings.splitByHeight)
                {
                    bar2.Width = (int)(twoPercent * this.Width);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(0, this.Height / 2);
                    else bar2.Location = new Point(this.Width - bar2.Width, this.Height / 2);
                }
                else
                {
                    bar2.Width = (int)(twoPercent * (this.Width) / 2);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(this.Width / 2, 0);
                    else bar2.Location = new Point((this.Width) - bar2.Width, 0);
                }
            }
            else
            {
                if (Settings.splitByHeight)
                {
                    bar2.Height = (int)(twoPercent * this.Height / 2);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(0, this.Height - bar2.Height);
                    else bar2.Location = new Point(0, this.Height / 2);
                }
                else
                {
                    bar2.Height = (int)(twoPercent * this.Height);
                    if (Settings.twoSettings.leftAligned) bar2.Location = new Point(this.Width / 2, this.Height - bar2.Height);
                    else bar2.Location = new Point(this.Width / 2, 0);
                }
            }
            #endregion
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (settingsEditor.Visible == false)
            {
                settingsEditor.Show();
                settingsEditor.Activate();
            }
            else
                settingsEditor.Activate();
        }
        void Bars_Click(object sender, EventArgs e)
        {
            if (settingsEditor.Visible == false) 
            {
                settingsEditor.Show();
                settingsEditor.Activate();
            }
        }
        void Bars2_Click(object sender, EventArgs e)
        {
            notifyIcon1.Visible = false;
            Environment.Exit(0);
        }

        private void cpuFadeTimer_Tick(object sender, EventArgs e)
        {
            if (Math.Abs(bar2.BackColor.R - twoNC.R) < 20 && Math.Abs(bar2.BackColor.G - twoNC.G) < 20 && Math.Abs(bar2.BackColor.B - twoNC.B) < 20)
                twoFadeTimer.Stop();
            else if (bar2.BackColor.R + twodr < 0 || bar2.BackColor.G + twodg < 0 || bar2.BackColor.B + twodb < 0 || bar2.BackColor.R + twodr > 255 || bar2.BackColor.G + twodg > 255 || bar2.BackColor.B + twodb > 255)
                twoFadeTimer.Stop();
            else
                bar2.BackColor = Color.FromArgb(bar2.BackColor.R + twodr, bar2.BackColor.G + twodg, bar2.BackColor.B + twodb);
        }

        private void ramFadeTimer_Tick(object sender, EventArgs e)
        {
            if (Math.Abs(bar1.BackColor.R - oneNC.R) < 20 && Math.Abs(bar1.BackColor.G - oneNC.G) < 20 && Math.Abs(bar1.BackColor.B - oneNC.B) < 20)
                oneFadeTimer.Stop();
            else if (bar1.BackColor.R + onedr < 0 || bar1.BackColor.G + onedg < 0 || bar1.BackColor.B + onedb < 0 || bar1.BackColor.R + onedr > 255 || bar1.BackColor.G + onedg > 255 || bar1.BackColor.B + onedb > 255)
                oneFadeTimer.Stop();
            else
                bar1.BackColor = Color.FromArgb(bar1.BackColor.R + onedr, bar1.BackColor.G + onedg, bar1.BackColor.B + onedb);
        }
    }
}